﻿using Microsoft.VisualStudio.TestTools.UnitTesting;
using AutoFakes;
using TestClasses;

namespace AutoFakesTest
{
    using System.Collections.Generic;
    using System.IO;
    using TestClasses.Fakes;

    [TestClass]
    public class IntegrationTests
    {
        [TestMethod]
        public void EndToEndTest()
        {
            // Arrange
            var autoStub = new AutoStub<ConnectorManager>();  // ConnectorManager has a dependency - IConnectorRepository
            var stubConnector = new StubIConnector();
            autoStub.Get<StubIConnectorRepository>().GetConnectorsString =
                filter => new List<IConnector> { stubConnector, stubConnector };

            // Act
            IList<IConnector> result = autoStub.Target.GetConnectors("filter");

            // Assert
            Assert.AreEqual(2, result.Count);
        }

        [TestMethod]
        public void YouCanInstantiateAnAutoFakeObject()
        {
            var autoStub = new AutoStub<ConcreteClassNoDependencyInjection>();
            Assert.IsNotNull(autoStub);
        }

        [TestMethod]
        public void AutoFakeHasAMemberTargetOfTypeTThatIsNotNull()
        {
            var autoStub = new AutoStub<ConcreteClassNoDependencyInjection>();
            ConcreteClassNoDependencyInjection target = autoStub.Target;

            Assert.IsNotNull(target);
        }

        [TestMethod]
        public void AutoFakeInstantiatesAnObjectWithoutADefaultConstructor()
        {
            var autoStub = new AutoStub<ConcreteClassWithOneInterfaceInConstructor>();
            ConcreteClassWithOneInterfaceInConstructor target = autoStub.Target;

            Assert.IsNotNull(target.MyInterface);
        }

        [TestMethod]
        public void AutoFakeChoosesBestConstructorWhenMoreThanOneIsAvailable()
        {
            var autoStub = new AutoStub<ConcreteClassWithTwoConstructors>();
            ConcreteClassWithTwoConstructors target = autoStub.Target;

            Assert.IsNotNull(target.MyInterface);
        }

        [TestMethod]
        public void AutoFakeCanConstructTargetWithMultipleCtorParameters()
        {
            var autoStub = new AutoStub<ConcreteClassWithThreeConstructors>();
            ConcreteClassWithThreeConstructors target = autoStub.Target;

            Assert.IsNotNull(target.MyInterface1);
            Assert.IsNotNull(target.MyInterface2);
        }

        [TestMethod]
        public void AutoFakeHandlesFailureOfNonStubbedClassesGracefully()
        {
            try
            {
                var autoStub = new AutoStub<ConcreteClassWithNonStubbedInterface>();
                // ReSharper disable UnusedVariable
                var target = autoStub.Target;
                // ReSharper restore UnusedVariable
                Assert.Fail();
            }
            catch (DependencyNotStubbedException)
            {
                Assert.IsTrue(true);
            }
        }

        [TestMethod]
        public void AutoFakeCanInstantiateAnObjectWithAnAbstractClassDependency()
        {
            var autoStub = new AutoStub<ConcreteClassWithAbstractClassDependency>();
            ConcreteClassWithAbstractClassDependency target = autoStub.Target;

            Assert.IsNotNull(target.MyAbstractClassDependency);
        }

        [TestMethod]
        public void AutoFakesCanRetrieveDependenciesForStubbingPurposes()
        {
            var autoStub = new AutoStub<ConcreteClassWithThreeConstructors>();
            ConcreteClassWithThreeConstructors target = autoStub.Target;
            var expected1 = target.MyInterface1;
            var expected2 = target.MyInterface2;

            var result1 = autoStub.Get<StubIInterface1>();
            var result2 = autoStub.Get<StubIInterface2>();

            Assert.AreSame(expected1, result1);
            Assert.AreSame(expected2, result2);
        }

        [TestMethod]
        public void AutoFakesCanHandleTwoDependenciesOfTheSameType()
        {
            var autoStub = new AutoStub<ConcreteClassWithTwoDependenciesOfSameType>();
            ConcreteClassWithTwoDependenciesOfSameType target = autoStub.Target;
            var expected1 = target.MyInterface1;
            var expected2 = target.MyInterface2;

            var result1 = autoStub.Get<IInterface1>(0);
            var result2 = autoStub.Get<IInterface1>(1);

            Assert.AreSame(expected1, result1);
            Assert.AreSame(expected2, result2);
            Assert.AreNotSame(result1, result2);
        }

        [TestMethod]
        public void GetThrowsExceptionWhenThereIsNoInstanceOfTheStubForASpecificIndex()
        {
            var autoStub = new AutoStub<ConcreteClassWithOneInterfaceInConstructor>();

            try
            {
                autoStub.Get<IInterface1>(1);
                Assert.Fail();
            }
            catch (InvalidDataException)
            {
                Assert.IsTrue(true);
            }

        }
    }
}
